﻿//+---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation, 2008
//
// File: ServiceDbProvider.cs
//
// Contents: Implementation of the data provider for the Service DB.
//
//
// Created by: waits 2008-10-20
//
//----------------------------------------------------------------------------


using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

using System.Data.SqlClient;
using System.Diagnostics;
using System.Globalization;

using System.Data.Linq;
using System.Linq;

using Microsoft.Reputation.Tests.FrontEndCommon.CSDBTableAdapters;

using MSAS.FrontEnd.DL;
using System.Configuration;
using System.Xml.Linq;


namespace Microsoft.Reputation.Tests.FrontEndCommon
{
    /// <summary>
    /// Holds the Service DB Objects and related info
    /// </summary>
    [Serializable]
    public class ServiceDbProvider
    {

        #region Service DB connection data members
        [NonSerialized] private bool m_isInit;
        [NonSerialized] private DbProcs dbConnection;
        private string dbServer;
        private string dbName;
        private string dbConnectionString;
        #endregion
        
        #region Service DB random objects tracking members
        [NonSerialized] private Dictionary<uint, IList<Guid>> m_randomGuids;
        #endregion

        /// <summary>
        /// Initializes the Service DB properties and objects
        /// </summary>
        /// <param name="server">Name of the server which hosts the Service DB</param>
        /// <param name="name">Database name, eg: ServiceDB or SDB</param>
        public ServiceDbProvider(string server, string name)
            : base()
        {
            if (String.IsNullOrEmpty(server))
            {
                throw new ArgumentException("Cannot use null or empty server name.", "server");
            }

            if (String.IsNullOrEmpty(name))
            {
                throw new ArgumentException("Cannot use null or empty database name.", "name");
            }

            this.m_isInit = false;
            this.dbServer = server;
            this.dbName = name;

            this.m_randomGuids = new Dictionary<uint, IList<Guid>>();
            this.dbConnectionString = this.BuildConnectionString(server, name);
            this.dbConnection = new DbProcs(this.BuildConnectionString(server, name));
        }


        #region Internal accessors
        private string DbServer
        {
            get
            {
                return dbServer;
            }
        }

        private string DbName
        {
            get
            {
                return dbName;
            }
        }

        private string DbConnectionString
        {
            get
            {
                return dbConnectionString;
            }
        }

        private bool IsInit
        {
            get
            {
                return this.m_isInit;
            }

            set
            {
                this.m_isInit = value;
            }
        }

        private IDictionary<uint, IList<Guid>> RandomGuids
        {
            get
            {
                return this.m_randomGuids;
            }
        }
        #endregion


        #region Connection creation members
        private string BuildConnectionString(string server, string dbName)
        {
            SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
            builder.DataSource = server;
            builder.InitialCatalog = dbName;
            builder.IntegratedSecurity = true;

            return builder.ToString();
        }
        #endregion
  

        #region Object reader members


        public MrsReputationObject GetObject(byte[] sha256)
        {
            using (ServiceDataContext db = new ServiceDataContext(this.dbConnectionString))
            {
                byte[] packedObject = db.Objects.Where(o => o.SHA256 == sha256).Select(o => o.ReputationBinary.ToArray()).SingleOrDefault();

                if (packedObject == null)
                {
                    Console.WriteLine("Object with SHA : {0} not found in the DB", Convertor.ConvertByteArrayToString(sha256));
                    return null;
                }

                UrfReputationObject obj = UrfReputationObject.Unpack(packedObject, MrsDeliveryServiceHelper.ExpirationTimes);
                
                return MrsReputationObject.TranslateFromUrfReputationObject(obj);
            }
        }

        //public MrsReputationObject GetObject(byte[] sha256)
        //{
        //    List<MrsCategoryReputation> reputationList = null;
        //    List<MrsMetadata> metadataList = null;
        //    string repuObj = string.Empty;
        //    uint expirationMins = 0;

        //    using (ServiceDataContext db = new ServiceDataContext(this.DbConnectionString))
        //    {
        //        XElement repuObjXml = (from obj in db.Objects
        //                               where obj.SHA256 == sha256
        //                               select obj.RepuObjects.RepuObj).FirstOrDefault();
        //        uint objType = (uint)(from obj in db.Objects where obj.SHA256 == sha256 select obj.ObjectTypeId).FirstOrDefault();
        //        Guid objGuid = (from obj in db.Objects where obj.SHA256 == sha256 select obj.ObjectGuid).FirstOrDefault();
        //        byte[] versionX = (from obj in db.Objects where obj.SHA256 == sha256 select obj.RowDbts).FirstOrDefault().ToArray();
        //        long objVersion = Convert.ToInt64(Convertor.ConvertByteArrayToString(versionX), 16);
                
        //        if (repuObjXml == null)
        //        {
        //            return null;
        //        }

        //        XmlDocument doc = new XmlDocument();
        //        doc.InnerXml = repuObjXml.ToString();
        //        XmlElement root = doc.DocumentElement;
        //        XmlNodeList reputationInfoList = root.SelectNodes(@"ReputationInfo/Category");
        //        XmlNodeList metadataInfoList = root.SelectNodes(@"MetadataInfo/Metadata");
        //        if (reputationInfoList.Count > 0)
        //        {
        //            expirationMins = MrsObjectConverter.GetExpirationMinsFromXml(reputationInfoList);
        //            reputationList = MrsObjectConverter.GetReputationFromXml(reputationInfoList);
        //        }
        //        if (metadataInfoList.Count > 0)
        //        {
        //            metadataList = MrsObjectConverter.GetMetadataFromXml(metadataInfoList);
        //        }

        //        MrsReputationObject res = new MrsReputationObject(objType, objGuid);

        //        res.Update(
        //            objVersion,
        //            metadataList,
        //            reputationList,
        //            expirationMins
        //        );

        //        return res;
        //    }
        //}

        /// <summary>
        /// Looks for the specified GUID in Service DB and returns its metadata and reputation info
        /// </summary>
        /// <param name="targetObj">GUID of the object for which data is needed</param>
        /// <returns>MrsObject containing Id, Metadata and Reputation info</returns>
        //public MrsReputationObject GetObject(Guid targetObj)
        //{
        //    List<MrsCategoryReputation> reputationList = null;
        //    List<MrsMetadata> metadataList = null;
        //    string repuObj = string.Empty;
        //    uint expirationMins = 0;

        //    string dbConnStr = this.BuildConnectionString(this.DbServer, this.DbName);

        //    ObjectTableAdapter objAdapter = new ObjectTableAdapter();
        //    objAdapter.Connection.ConnectionString = dbConnStr;

        //    //Gets the object info for the specified GUID
        //    CSDB.ObjectDataTable objs = objAdapter.GetDataByGuid(targetObj);
        //    if (objs.Count == 0)
        //    {
        //        return null;
        //    }

        //    Debug.Assert(objs.Count == 1, "Should only have one object match for a GUID.");

        //    repuObj = objs[0].RepuObj;

        //    if (false == string.IsNullOrEmpty(repuObj))
        //    {
        //        //Building the reputation object from the xml obtained from the ServiceDB using the GUID
        //        XmlDocument doc = new XmlDocument();
        //        doc.InnerXml = repuObj;
        //        XmlElement root = doc.DocumentElement;
        //        XmlNodeList reputationInfoList = root.SelectNodes(@"ReputationInfo/Category");
        //        XmlNodeList metadataInfoList = root.SelectNodes(@"MetadataInfo/Metadata");
        //        if (reputationInfoList.Count > 0)
        //        {
        //            expirationMins = MrsObjectConverter.GetExpirationMinsFromXml(reputationInfoList);
        //            reputationList = MrsObjectConverter.GetReputationFromXml(reputationInfoList);
        //        }
        //        if (metadataInfoList.Count > 0)
        //        {
        //            metadataList = MrsObjectConverter.GetMetadataFromXml(metadataInfoList);
        //        }
        //    }

        //    MrsReputationObject res = new MrsReputationObject((uint)(objs[0].ObjectTypeId), objs[0].ObjectGuid);

        //    res.Update(
        //        objs[0].ObjectVersionId,
        //        metadataList, 
        //        reputationList,
        //        expirationMins
        //    );

        //    return res;
        //}

        //public bool GetObjects(IList<byte[]> requests, out IList<MrsReputationObject> results)
        //{
        //    bool operStatus = true;
        //    results = new List<MrsReputationObject>();
        
        //    foreach (byte[] reqSha in requests)
        //    {
        //        MrsReputationObject obj = GetObject(reqSha);
        //        if (null == obj)
        //        {
        //            operStatus = false;
        //        }
        //        else
        //        {
        //            results.Add(obj);
        //        }
        //    }

        //    return operStatus;
        //}

        /// <summary>
        /// Gets objects from DB in test MrsReputationObject form for the list of Object Guids specified
        /// </summary>
        /// <param name="requests">List of Guids to look for in the DB</param>
        /// <param name="results">List of corresponding reputation objects</param>
        /// <returns>True if the operation was successful else False</returns>
        //public bool GetObjects(IList<Guid> requests, out IList<MrsReputationObject> results)
        //{
        //    bool operStatus = true;
        //    results = new List<MrsReputationObject>();

        //    foreach (Guid reqGuid in requests)
        //    {
        //        MrsReputationObject obj = GetObject(reqGuid);
        //        if (null == obj)
        //        {
        //            operStatus = false;
        //        }
        //        else
        //        {
        //            results.Add(obj);
        //        }
        //    }

        //    return operStatus;
        //}

        /// <summary>
        /// Gets random objects from the DB for the type and count specified
        /// </summary>
        /// <param name="objType">Type of object to look for in the DB - URL, File etc</param>
        /// <param name="count">Number of random objects to look for</param>
        /// <param name="results">List of random reputation objects</param>
        /// <returns>True if the operation was successful else False</returns>
        //public bool GetRandomObjects(uint objType, int count, out IList<MrsReputationObject> results)
        //{
        //    bool operStatus = true;
        //    int remaining;
        //    results = null;

        //    if (false == MrsObjectTypes.IsValidType(objType))
        //    {
        //        throw new ArgumentOutOfRangeException("ERROR! Invalid object type requested");
        //    }

        //    if (count <= 0)
        //    {
        //        throw new ArgumentOutOfRangeException("ERROR! Number of objects requests in invalid");
        //    }

        //    IList<Guid> guidList = dbConnection.GetRandomObjects((byte)objType, count, null, out remaining);

        //    //If the GUIDs for a type returned from DB are less than total requested and there are no more such objects in DB
        //    if ((remaining == 0) && (guidList.Count < count))
        //    {
        //        throw new Exception("Insufficient objects in DB to fulfill requests");
        //    }

        //    // Get list of objects from DB for the given list of GUIDs
        //    operStatus = GetObjects(guidList, out results);

        //    return operStatus;
        //}
        #endregion
    }
}
